home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-02
/
tpmemo.zip
/
TPMEMO.PAS
< prev
Wrap
Pascal/Delphi Source File
|
1993-01-04
|
58KB
|
1,949 lines
{$S-,R-,V-,I-,B-,F+}
{$IFNDEF Ver40}
{$I OPLUS.INC}
{$I AMINUS.INC}
{$ENDIF}
{$I TPDEFINE.INC}
{*********************************************************}
{* TPMEMO.PAS 1.0 *}
{* Copyright (c) TurboPower Software 1988. *}
{* Portions copyright (c) Sunny Hill Software 1985, 1986 *}
{* and used under license to TurboPower Software *}
{* All rights reserved. *}
{*********************************************************}
unit TpMemo;
{-Memo field editor}
interface
uses
TpCrt,
{$IFDEF UseMouse}
TpMouse,
{$ENDIF}
TpCmd,
TpString;
{.F-}
const
EMnone = 00; {Not a command}
EMchar = 01; {A character to enter the string}
EMctrlChar = 02; {Accept control character}
EMenter = 03; {New line}
EMquit = 04; {Quit editing}
EMrestore = 05; {Restore line and continue}
EMhome = 06; {Cursor to beginning of line}
EMend = 07; {Cursor to end of line}
EMleft = 08; {Cursor left by one character}
EMright = 09; {Cursor right by one character}
EMup = 10; {Cursor up one line}
EMdown = 11; {Cursor down one line}
EMscrollUp = 12; {Scroll display up one line}
EMscrollDown = 13; {Scroll display down one line}
EMpageUp = 14; {Scroll display up one page}
EMpageDown = 15; {Scroll display down one page}
EMscreenTop = 16; {Cursor to top of screen}
EMscreenBot = 17; {Cursor to bottom of screen}
EMtopOfFile = 18; {Cursor to top of file}
EMendOfFile = 19; {Cursor to bottom of file}
EMwordLeft = 20; {Cursor left one word}
EMwordRight = 21; {Cursor right one word}
EMback = 22; {Backspace one character}
EMdel = 23; {Delete current character}
EMdelEol = 24; {Delete from cursor to end of line}
EMdelLine = 25; {Delete entire line}
EMdelWord = 26; {Delete word to right of cursor}
EMtab = 27; {Tab}
EMins = 28; {Toggle insert mode}
EMindent = 29; {Toggle auto-indent mode}
EMwordWrap = 30; {Toggle word wrap}
EMreformatP = 31; {Reformat paragraph}
EMreformatG = 32; {Global reformat}
EMhelp = 33; {Invoke help routine}
EMmouse = 34; {Mouse select}
EMuser0 = 35; {user-defined exit commands}
EMuser1 = 36;
EMuser2 = 37;
EMuser3 = 38;
EMuser4 = 39;
EMuser5 = 40;
EMuser6 = 41;
EMuser7 = 42;
EMuser8 = 43;
EMuser9 = 44;
EMuser10 = 45;
EMuser11 = 46;
EMuser12 = 47;
EMuser13 = 48;
EMuser14 = 49;
EMuser15 = 50;
EMuser16 = 51;
EMuser17 = 52;
EMuser18 = 53;
EMuser19 = 54;
{.F+}
const
MaxLineLength : Byte = 127; {!do not make larger than 127!}
{error message codes}
tmBufferFull = 1; {edit buffer is full}
tmLineTooLong = 2; {line too long, CRLF inserted}
tmTooManyLines = 3; {max line limit would be exceeded}
tmOverLineLimit = 4; {max line limit already exceeded}
{if True, reformatting routine treats blank space at start of line as
signalling the start of a new paragraph}
IndentStartsParagraph : Boolean = False;
const
AllowTruncation : Boolean = True; {read partial files?}
type
EMtype = EMnone..EMuser19;
EMbuffer = array[1..65521] of Char;
EMcontrolBlock =
record
UserData : Pointer; {reserved for user (ID number perhaps)}
XL, YL, XH, YH : Byte; {coordinates for edit window}
BufPtr : ^EMbuffer; {pointer to text buffer}
BufSize : Word; {size of buffer}
MaxLines : Integer; {maximum number of lines}
TotalBytes : Word; {bytes in buffer}
TotalLines : Integer; {lines in buffer}
LineAtTop : Integer; {line at top of edit window}
BufPosTop : Word; {index into buffer for start of line at top}
CurLine : Integer; {line number of current line}
BufPos : Word; {index into buffer for start of current line}
CurCol : Byte; {position of cursor within current line}
ColDelta : Byte; {for horizontal scrolling}
KnownLine : Integer; {used to speed up scrolling/searching}
KnownOfs : Word; {" " " " "}
TAttr : Byte; {attribute for normal text}
CAttr : Byte; {attribute for control characters}
InsertMode : Boolean; {True if in insert mode}
IndentMode : Boolean; {True if in auto-indent mode}
ReadOnlyMode : Boolean;{True if in read-only mode}
WordWrap : Boolean; {True if word wrap is on}
Modified : Boolean; {True if edits have been made}
TabDelta : Byte; {distance between tab stops}
Margin : Byte; {right margin}
HelpTopic : Word; {help topic}
end;
MemoStatusType = (
mstOK, mstNotFound, mstInvalidName, mstReadError, mstTooLarge,
mstTruncated, mstCreationError, mstWriteError, mstCloseError);
const
MemoKeyPtr : Pointer = nil; {pointer to routine to return next keystroke}
MemoHelpPtr : Pointer = nil; {pointer to routine to display help}
MemoStatusPtr : Pointer = nil; {pointer to routine to display status line}
MemoErrorPtr : Pointer = nil; {pointer to routine to display error messages}
HelpForMemo = HelpForXXXX1; {special code for help routine calls}
const
{the commands in this set are disallowed in read-only mode}
DisallowedInReadOnlyMode : set of EMtype =
[EMchar..EMenter, EMrestore, EMback..EMreformatG];
const
{used only by MemoStatus}
StatusRow : Byte = 2; {default to second line of screen for status line}
StatusAttr : Byte = $F; {attribute for status line}
const
{used only by MemoError}
ErrorRow : Byte = 1; {default to top line of screen for error messages}
ErrorAttr : Byte = $F; {attribute for error message line}
{$IFDEF UseMouse}
const
{True if mouse support is enabled}
MemoMouseEnabled : Boolean = False;
{$ENDIF}
{.F+}
procedure InitControlBlock(var EMCB : EMcontrolBlock;
XLow, YLow, XHigh, YHigh : Byte;
TextAttr, CtrlAttr : Byte;
InsertOn, IndentOn, WordWrapOn : Boolean;
TabSize : Byte; HelpIndex : Word;
RightMargin : Byte; LineLimit : Integer;
BufferSize : Word; var Buffer);
{-Initialize a memo editor control block}
function EditMemo(var EMCB : EMcontrolBlock;
ReadOnly : Boolean;
var CmdList) : EMtype;
{-Edit a buffer filled with text}
procedure MemoStatus(var EMCB : EMcontrolBlock);
{-Display status line}
procedure MemoError(var EMCB : EMcontrolBlock; ErrorCode : Word);
{-Display error message and wait for key press}
function AddMemoCommand(Cmd : EMtype; NumKeys : Byte; Key1, Key2 : Word) : Boolean;
{-Add a new command key assignment or change an existing one}
{$IFDEF UseMouse}
procedure EnableMemoMouse;
{-Enable mouse support in TPMEMO}
procedure DisableMemoMouse;
{-Disable mouse support in TPMEMO}
{$ENDIF}
{file handling routines}
function ReadMemoFile(var Buffer; BufferSize : Word;
FName : string; var FSize : LongInt) : MemoStatusType;
{-Read a file into Buffer, returning a status code}
function SaveMemoFile(var EMCB : EMcontrolBlock; FName : string;
MakeBackup : Boolean) : MemoStatusType;
{-Save the current file in the text buffer associated with EMCB}
{.F-}
const
{Keystroke to command mapping}
MemoKeyMax = 250; {last available slot in MemoKeySet}
{ID string for installation programs}
MemoKeyID : string[16] = 'tpmemo key array';
{default key assignments}
MemoKeySet : array[0..MemoKeyMax] of Byte = (
{length keys command type key sequence}
3, $00, $00, EMquit, {^Break}
3, $00, $13, EMreformatG, {AltR}
3, $00, $3B, EMhelp, {F1}
3, $00, $47, EMhome, {Home}
3, $00, $48, EMup, {Up}
3, $00, $49, EMpageUp, {PgUp}
3, $00, $4B, EMleft, {Left}
3, $00, $4D, EMright, {Right}
3, $00, $4F, EMend, {End}
3, $00, $50, EMdown, {Down}
3, $00, $51, EMpageDown, {PgDn}
3, $00, $52, EMins, {Ins}
3, $00, $53, EMdel, {Del}
3, $00, $73, EMwordLeft, {^Left}
3, $00, $74, EMwordRight, {^Right}
3, $00, $75, EMscreenBot, {^End}
3, $00, $76, EMendOfFile, {^PgDn}
3, $00, $77, EMscreenTop, {^Home}
3, $00, $84, EMtopOfFile, {^PgUp}
2, $01, EMwordLeft, {^A}
2, $02, EMreformatP, {^B}
2, $03, EMpageDown, {^C}
2, $04, EMright, {^D}
2, $05, EMup, {^E}
2, $06, EMwordRight, {^F}
2, $07, EMdel, {^G}
2, $08, EMback, {^H, Bksp}
2, $09, EMtab, {^I, Tab}
2, $0D, EMenter, {^M, Enter}
2, $10, EMctrlChar, {^P}
2, $12, EMpageUp, {^R}
2, $13, EMleft, {^S}
2, $14, EMdelWord, {^T}
2, $16, EMins, {^V}
2, $17, EMscrollUp, {^W}
2, $18, EMdown, {^X}
2, $19, EMdelLine, {^Y}
2, $1A, EMscrollDown, {^Z}
2, $1B, EMquit, {Esc}
2, $7F, EMback, {^Bksp}
3, $0F, $09, EMindent, {^O^I}
3, $0F, $17, EMwordWrap, {^O^W}
3, $11, $03, EMendOfFile, {^Q^C}
3, $11, $04, EMend, {^Q^D}
3, $11, $05, EMscreenTop, {^Q^E}
3, $11, $0C, EMrestore, {^Q^L}
3, $11, $12, EMtopOfFile, {^Q^R}
3, $11, $13, EMhome, {^Q^S}
3, $11, $18, EMscreenBot, {^Q^X}
3, $11, $19, EMdelEol, {^Q^Y}
{$IFDEF UseMouse}
3, $00, $EF, EMmouse, {click left = mouse select}
3, $00, $EE, EMquit, {click right = ESC}
3, $00, $ED, EMhelp, {click both = help}
{$ELSE}
0, 0, {180}
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {190}
{$ENDIF}
{-----------pad to end of array----------}
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {200}
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {210}
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {220}
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {230}
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {240}
0, 0, 0, 0, 0, 0, 0, 0, 0, 0); {250}
{.F+}
{routines intended primarily for internal use, but which might be used to
implement user-defined commands or for other purposes}
function FindLineIndex(var EMCB : EMcontrolBlock; LineNum : Integer) : Word;
{-Return the index into the edit buffer for the specified line number.
LineNum must be <= EMCB.TotalLines.}
function FindLineLength(var EMCB : EMcontrolBlock; LineNum : Integer) : Word;
{-Find the length of the specified line}
procedure InitBufferState(var EMCB : EMcontrolBlock;
BufferSize : Word; var Buffer);
{-Initialize the edit buffer status fields in a control block}
procedure GetLine(var EMCB : EMcontrolBlock; var S : string; LineNum : Integer);
{-Get the LineNum'th line from the buffer for the specified control block
and store it in S. If line is longer than 255 characters, only the first
255 characters will be loaded into S.}
procedure DrawLine(var EMCB : EMcontrolBlock; St : String; LineNum : Integer);
{-Draw the string St, which represents the specified line number}
procedure FastWriteCtrl(St : String; Row, Col, Attr, Ctrl : Byte);
{-Write St at Row,Col in Attr (video attribute) without snow.
Control characters displayed in Ctrl as upper-case letters}
{==========================================================================}
implementation
const
SafetyMargin = 2;
CtrlZ : Char = ^Z;
CRLF : array[1..2] of Char = ^M^J;
SearchFailed = $FFFF;
{$L TPMEMO}
procedure FastWriteCtrl(St : String; Row, Col, Attr, Ctrl : Byte);
{-Write St at Row,Col in Attr (video attribute) without snow.
Control characters displayed in Ctrl as upper-case letters}
external;
function Scan(Limit : Integer; Ch : Char; T : Pointer) : Integer;
{-Scan limit chars for Ch; Ch not found if Result=Limit}
external;
procedure HelpRoutine(UnitCode : Byte; IdPtr : Pointer; HelpIndex : Word);
{-Call routine pointed to by MemoHelpPtr}
inline(
$FF/$1E/>MemoHelpPtr); {call dword ptr [>MemoHelpPtr]}
procedure StatusRoutine(var EMCB : EMcontrolBlock);
{-Call routine pointed to by MemoStatusPtr}
inline(
$FF/$1E/>MemoStatusPtr); {call dword ptr [>MemoStatusPtr]}
procedure ErrorRoutine(var EMCB : EMcontrolBlock; ErrorCode : Word);
{-Call routine pointed to by MemoErrorPtr}
inline(
$FF/$1E/>MemoErrorPtr); {call dword ptr [>MemoErrorPtr]}
function GetKey : Word;
{-Call routine pointed to by MemoKeyPtr}
inline(
$FF/$1E/>MemoKeyPtr); {call dword ptr [>MemoKeyPtr]}
{$IFDEF UseMouse}
procedure HideMousePrim(var MouseState : Boolean);
{-Save state of mouse cursor in MouseState and hide it}
begin
MouseState := MouseCursorOn;
HideMouse;
end;
procedure ShowMousePrim(MouseOn : Boolean);
{-Hide or unhide the mouse cursor}
begin
if MouseOn then
ShowMouse
else
HideMouse;
end;
{$ENDIF}
procedure InitBufferState(var EMCB : EMcontrolBlock;
BufferSize : Word; var Buffer);
{-Initialize the edit buffer status fields in a control block}
var
I, J : Word;
Buf : EMbuffer absolute Buffer;
begin
with EMCB do begin
{reset edit buffer state variables}
Modified := False;
BufSize := BufferSize;
BufPtr := @Buffer;
BufPos := 1;
BufPosTop := 1;
KnownLine := 1;
KnownOfs := 1;
CurLine := 1;
CurCol := 1;
ColDelta := 0;
LineAtTop := 1;
{find end of text buffer}
I := Search(Buffer, BufferSize, CtrlZ, 1);
if (I = SearchFailed) or (I = 0) then begin
{buffer is empty}
TotalBytes := 1;
TotalLines := 1;
Buf[1] := CtrlZ;
end
else begin
TotalBytes := I+1;
{count total number of rows}
TotalLines := 1;
I := 1;
repeat
J := Search(Buf[I], Succ(TotalBytes-I), CRLF, 2);
if J <> SearchFailed then begin
Inc(TotalLines);
Inc(I, J+2);
end;
until (J = SearchFailed) or (I >= TotalBytes);
end;
end;
end;
procedure InitControlBlock(var EMCB : EMcontrolBlock;
XLow, YLow, XHigh, YHigh : Byte;
TextAttr, CtrlAttr : Byte;
InsertOn, IndentOn, WordWrapOn : Boolean;
TabSize : Byte; HelpIndex : Word;
RightMargin : Byte; LineLimit : Integer;
BufferSize : Word; var Buffer);
{-Initialize a memo editor control block}
begin
with EMCB do begin
XL := XLow;
YL := YLow;
XH := XHigh;
YH := YHigh;
TAttr := TextAttr;
CAttr := CtrlAttr;
InsertMode := InsertOn;
IndentMode := IndentOn;
ReadOnlyMode := False;
WordWrap := WordWrapOn;
TabDelta := TabSize;
if RightMargin = 0 then
Margin := Succ(XH-XL)
else if RightMargin > MaxLineLength then
Margin := MaxLineLength
else
Margin := RightMargin;
if LineLimit <= 0 then
MaxLines := MaxInt
else
MaxLines := LineLimit;
HelpTopic := HelpIndex;
{initialize TotalLines, TotalBytes, etc.}
InitBufferState(EMCB, BufferSize, Buffer);
end;
end;
procedure MemoStatus(var EMCB : EMcontrolBlock);
{-Display status line}
const
OnOff : array[Boolean] of string[3] = ('Off', 'On ');
Save : array[Boolean] of string[4] = (' ', 'SAVE');
StatusLine : string[80] =
{ 1 2 3 4 5 6 7 8}
{12345678901234567890123456789012345678901234567890123456789012345678901234567890}
' Line: xxxxx Column: xxx 100% Insert: Off Indent: Off Word wrap: Off SAVE ';
var
S : string[5];
{$IFDEF UseMouse}
SaveMouse : Boolean;
{$ENDIF}
begin
with EMCB do begin
{insert line number}
S := Long2Str(CurLine);
S := Pad(S, 5);
Move(S[1], StatusLine[8], 5);
{insert column number}
S := Long2Str(CurCol);
S := Pad(S, 3);
Move(S[1], StatusLine[23], 3);
{insert percentage of buffer used}
S := Real2Str(Trunc((TotalBytes*100.0)/(BufSize-SafetyMargin)), 3, 0);
Move(S[1], StatusLine[28], 3);
{insert remaining fields}
Move(OnOff[InsertMode][1], StatusLine[42], 3);
Move(OnOff[IndentMode][1], StatusLine[55], 3);
Move(OnOff[WordWrap][1], StatusLine[71], 3);
Move(Save[Modified][1], StatusLine[76], 4);
{$IFDEF UseMouse}
HideMousePrim(SaveMouse);
{$ENDIF}
{display status line}
FastWrite(StatusLine, StatusRow, 1, StatusAttr);
{$IFDEF UseMouse}
ShowMousePrim(SaveMouse);
{$ENDIF}
end;
end;
procedure MemoError(var EMCB : EMcontrolBlock; ErrorCode : Word);
{-Display error message and wait for key press}
var
S : string[80];
I : Word;
{$IFDEF UseMouse}
SaveMouse : Boolean;
{$ENDIF}
begin
case ErrorCode of
tmBufferFull :
S := 'Edit buffer is full';
tmLineTooLong :
S := 'Line too long, carriage return inserted';
tmTooManyLines :
S := 'Limit on number of lines has been reached';
tmOverLineLimit :
S := 'Limit on number of lines has been exceeded';
else
S := 'Unknown error';
end;
S := S+'. Press any key...';
{$IFDEF UseMouse}
HideMousePrim(SaveMouse);
{$ENDIF}
{display error message}
FastWrite(Pad(S, ScreenWidth), ErrorRow, 1, ErrorAttr);
{$IFDEF UseMouse}
ShowMousePrim(SaveMouse);
{$ENDIF}
{flush the keyboard buffer}
while KeyPressed do
I := GetKey;
{wait for key press}
I := GetKey;
{clear error message line}
FastWrite(CharStr(' ', ScreenWidth), ErrorRow, 1, ErrorAttr);
end;
function FindLineIndex(var EMCB : EMcontrolBlock; LineNum : Integer) : Word;
{-Return the index into the edit buffer for the specified line number}
var
I : Integer;
begin
with EMCB do begin
if LineNum = 1 then begin
KnownLine := 1;
KnownOfs := 1;
end
else if LineNum >= KnownLine then
while KnownLine < LineNum do begin
I := Succ(TotalBytes-KnownOfs);
if I < 0 then
I := MaxInt;
Inc(KnownOfs, Succ(Scan(I, ^J, @BufPtr^[KnownOfs])));
Inc(KnownLine);
end
else begin
{linenum < knownline, search backwards}
Dec(KnownOfs, 2);
while KnownLine > LineNum do begin
I := KnownOfs;
if I < 0 then
I := MaxInt;
Inc(Integer(KnownOfs), Pred(Scan(-I, ^J, @BufPtr^[KnownOfs])));
Dec(KnownLine);
end;
{point to start of next line}
Inc(KnownOfs, 2);
end;
FindLineIndex := KnownOfs;
end;
end;
function FindLineLength(var EMCB : EMcontrolBlock; LineNum : Integer) : Word;
{-Find the length of the specified line}
var
I, J : Word;
begin
with EMCB do
if LineNum > TotalLines then
FindLineLength := 0
else begin
{find starting index for line}
J := FindLineIndex(EMCB, LineNum);
{calculate length}
I := Search(BufPtr^[J], Succ(TotalBytes-J), CRLF, 2);
if I = SearchFailed then
FindLineLength := TotalBytes-J
else
FindLineLength := I;
end;
end;
procedure GetLine(var EMCB : EMcontrolBlock; var S : string; LineNum : Integer);
{-Get the LineNum'th line from the buffer for the specified control block,
and store it in S}
var
I, J : Word;
SLen : Byte absolute S;
begin
with EMCB do
if LineNum > TotalLines then
SLen := 0
else begin
{find starting index and length for line}
J := FindLineIndex(EMCB, LineNum);
I := FindLineLength(EMCB, LineNum);
{truncate if line is too long}
if I > 255 then
SLen := 255
else
SLen := I;
Move(BufPtr^[J], S[1], SLen);
end;
end;
procedure DrawLine(var EMCB : EMcontrolBlock; St : String; LineNum : Integer);
{-Draw the string St, which represents the specified line number}
var
StLen : Byte absolute St;
WinWidth : Byte;
begin
{calculate screen row}
Dec(LineNum, Pred(EMCB.LineAtTop));
Inc(LineNum, Pred(EMCB.YL));
with EMCB do begin
WinWidth := Succ(XH-XL);
{adjust for ColDelta}
if (ColDelta > 0) and (StLen > 0) then
if ColDelta >= StLen then
StLen := 0
else begin
Move(St[ColDelta+1], St[1], StLen-ColDelta);
Dec(StLen, ColDelta);
end;
end;
{pad the end of the string}
if StLen < WinWidth then
FillChar(St[Succ(StLen)], WinWidth-StLen, ' ');
{change the length}
StLen := WinWidth;
{draw the string}
with EMCB do
if CAttr = TAttr then
FastWrite(St, LineNum, XL, TAttr)
else
FastWriteCtrl(St, LineNum, XL, TAttr, CAttr);
end;
function EditMemo(var EMCB : EMcontrolBlock;
ReadOnly : Boolean;
var CmdList) : EMtype;
{-Edit a buffer filled with text}
type
CmdListType = array[1..100] of EMtype;
var
ChWord : Word;
Ch : Char absolute ChWord;
OldSt, St : string; {text of current line}
OldCol : Byte;
OldModified : Boolean;
StLen : Byte absolute St;
I, J : Word;
CursorSL : Word;
CursorXY : Word;
SaveBreak : Boolean;
ForceRedraw : Boolean;
DoingChars : Boolean;
Done, OK : Boolean;
WinWidth : Byte;
EMC : EMtype;
UserCmdList : CmdListType absolute CmdList;
NextUserCmd : Word;
{$IFDEF UseMouse}
SaveWaitState : Boolean;
SaveMouse : Boolean;
{$ENDIF}
procedure CallErrorRoutine(Code : Integer);
{-Call the user-defined error routine}
begin
if MemoErrorPtr <> nil then
ErrorRoutine(EMCB, Code);
end;
procedure TrimSpaces;
{-Trim trailing blanks from current line}
begin
while St[StLen] = ' ' do
Dec(StLen);
end;
function InsertOK(N : Integer) : Boolean;
{-Return True if OK to insert N bytes into the edit buffer. Calls user
error handler if not OK.}
var
I : LongInt;
begin
with EMCB do begin
{allow a safety margin}
I := TotalBytes+SafetyMargin;
{calculate actual TotalBytes+N}
Inc(I, LongInt(N)+(LongInt(StLen)-Length(OldSt)));
if I <= BufSize then
InsertOK := True
else begin
InsertOK := False;
CallErrorRoutine(tmBufferFull);
end;
end;
end;
procedure ToggleInsertMode;
{-Toggle between insert and overtype mode, keeping BIOS keyboard flag up
to date}
var
BiosKbdFlag : Byte absolute $0040 : $0017;
begin
with EMCB do begin
{toggle insert flag}
InsertMode := not InsertMode;
{use fat cursor if inserting}
if InsertMode then begin
FatCursor;
BiosKbdFlag := BiosKbdFlag or $80;
end
else begin
NormalCursor;
BiosKbdFlag := BiosKbdFlag and $7F;
end;
end;
end;
procedure DrawCurrentLine;
{-Draw the current line}
{$IFDEF UseMouse}
var
SaveMouse : Boolean;
{$ENDIF}
begin
{$IFDEF UseMouse}
HideMousePrim(SaveMouse);
{$ENDIF}
{draw the current line}
DrawLine(EMCB, St, EMCB.CurLine);
{$IFDEF UseMouse}
ShowMousePrim(SaveMouse);
{$ENDIF}
end;
procedure RedrawScreen;
{-Redraw entire screen}
var
I, J : Integer;
S : String;
{$IFDEF UseMouse}
SaveMouse : Boolean;
{$ENDIF}
begin
{$IFDEF UseMouse}
HideMousePrim(SaveMouse);
{$ENDIF}
with EMCB do begin
J := LineAtTop+(YH-YL);
for I := LineAtTop to J do begin
if (I = CurLine) then
DrawLine(EMCB, St, I)
else begin
GetLine(EMCB, S, I);
DrawLine(EMCB, S, I);
end;
end;
end;
{$IFDEF UseMouse}
ShowMousePrim(SaveMouse);
{$ENDIF}
ForceRedraw := False;
end;
procedure SaveCurrentLine(Trim : Boolean);
{-Patch the current line back into place}
var
I, J : Word;
K : Integer;
begin
with EMCB do begin
if Trim then
TrimSpaces;
if St = OldSt then
Exit;
{find the actual length of the current line}
I := BufPos;
J := FindLineLength(EMCB, CurLine);
{calculate difference in size}
K := Integer(StLen)-J;
if K > 0 then
{make room for new text}
Move(BufPtr^[I], BufPtr^[I+K], Succ(TotalBytes-I))
else
{delete excess characters}
Move(BufPtr^[I-K], BufPtr^[I], Succ(TotalBytes-I)+K);
{insert the text}
Move(St[1], BufPtr^[I], StLen);
Inc(TotalBytes, K);
KnownLine := LineAtTop;
KnownOfs := BufPosTop;
OldSt := St;
Modified := True;
OldModified := True;
end;
end;
procedure ScrollDisplay(Lines : Integer);
{-Scroll the editing window up or down}
var
S : string;
SaveTextAttr : Byte;
I, J, K : Integer;
{$IFDEF UseMouse}
SaveMouse : Boolean;
{$ENDIF}
begin
if Lines = 0 then
Exit;
with EMCB do begin
SaveTextAttr := TextAttr;
TextAttr := TAttr;
{$IFDEF UseMouse}
HideMousePrim(SaveMouse);
{$ENDIF}
if Lines < 0 then
ScrollWindowDown(XL, YL, XH, YH, -Lines)
else
ScrollWindowUp(XL, YL, XH, YH, Lines);
BufPosTop := FindLineIndex(EMCB, LineAtTop+Lines);
Inc(LineAtTop, Lines);
if Lines < 0 then begin
J := LineAtTop;
K := Pred(J-Lines);
end
else begin
J := LineAtTop+(YH-YL)-Pred(Lines);
K := Pred(J+Lines);
end;
{draw the line(s) replacing the one(s) that scrolled off}
for I := J to K do begin
GetLine(EMCB, S, I);
DrawLine(EMCB, S, I);
end;
{$IFDEF UseMouse}
ShowMousePrim(SaveMouse);
{$ENDIF}
TextAttr := SaveTextAttr;
end;
end;
function TooManyLinesCheck : Boolean;
{-Check to see if there are too many lines}
begin
with EMCB do
if Word(TotalLines) >= Word(MaxLines) then begin
CallErrorRoutine(tmTooManyLines);
OK := False;
TooManyLinesCheck := True;
end
else
TooManyLinesCheck := False;
end;
procedure InsLinePrim(LineNum, Col : Integer);
{-Primitive routine to insert a line break}
var
I, J : Word;
begin
with EMCB do begin
if TooManyLinesCheck then
Exit;
{find the place to insert the line break}
I := FindLineIndex(EMCB, LineNum)+Pred(Col);
{see if we need to trim some blanks}
J := Pred(I);
while (J > 0) and (BufPtr^[J] = ' ') do
Dec(J);
Inc(J);
if J <> I then begin
{see if there's room}
OK := InsertOK(2-(I-J));
if not OK then
Exit;
{make room for a CRLF}
Move(BufPtr^[I], BufPtr^[J+2], Succ(TotalBytes-I));
{insert the CRLF}
Move(CRLF, BufPtr^[J], 2);
{adjust counters}
Inc(TotalLines);
TotalBytes := (TotalBytes+2)-(I-J);
end
else begin
{see if there's room}
OK := InsertOK(2);
if not OK then
Exit;
{make room for a CRLF}
Move(BufPtr^[I], BufPtr^[I+2], Succ(TotalBytes-I));
{insert the CRLF}
Move(CRLF, BufPtr^[I], 2);
{adjust counters}
Inc(TotalLines);
Inc(TotalBytes, 2);
end;
Modified := True;
end;
end;
procedure LoadLine(LineNum : Integer; Truncate : Boolean);
{-Load the specified line}
var
I, J, K, N, Max : Word;
begin
with EMCB do begin
{find the line we're moving to}
BufPos := FindLineIndex(EMCB, LineNum);
CurLine := LineNum;
{find the length of the line}
I := FindLineLength(EMCB, LineNum);
{calc max length of line}
if Truncate then
Max := MaxLineLength
else
Max := 255;
{insert carriage return if line is too long}
if I > Max then begin
{determine where to break the line}
K := Max;
N := FindLineIndex(EMCB, LineNum);
J := N+Pred(K);
while (J > N) and (BufPtr^[J] <> ' ') do begin
Dec(J);
Dec(K);
end;
if J = N then
K := Max;
{try to break the line}
Inc(MaxLines);
InsLinePrim(LineNum, K);
Dec(MaxLines);
if not OK then begin
{something overflowed--force the line break}
Inc(N, K);
BufPtr^[N] := ^M;
BufPtr^[N+1] := ^J;
Inc(TotalLines);
end;
{report the break}
CallErrorRoutine(tmLineTooLong);
{force screen to be redrawn}
ForceRedraw := True;
{recalculate the length}
I := FindLineLength(EMCB, LineNum);
end;
{load the line into St and OldSt}
StLen := I;
Move(BufPtr^[BufPos], St[1], StLen);
OldSt := St;
OldCol := CurCol;
Modified := OldModified;
end;
end;
procedure GotoLine(LineNum : Integer; Trim : Boolean);
{-Save the current line and move the cursor to the LineNum'th line}
var
I : Word;
begin
with EMCB do begin
{don't go too far}
if LineNum > TotalLines then
LineNum := TotalLines;
{save the line we've been editing}
SaveCurrentLine(Trim);
{scroll the display if necessary}
if LineNum < LineAtTop then
ScrollDisplay(LineNum-LineAtTop)
else begin
I := LineAtTop+(YH-YL);
if LineNum > I then
ScrollDisplay(LineNum-I);
end;
{load the line}
LoadLine(LineNum, Trim);
end;
end;
procedure DelLinePrim(LineNum : Integer);
{-Primitive routine to delete a line}
var
I, J : Word;
begin
with EMCB do begin
{find the line we're deleting}
I := FindLineIndex(EMCB, LineNum);
{find the length of the line}
J := Search(BufPtr^[I], Succ(TotalBytes-I), CRLF, 2);
if J = SearchFailed then
J := TotalBytes-BufPos
else
Inc(J, 2);
{delete it}
Move(BufPtr^[I+J], BufPtr^[I], Succ(TotalBytes-I)-J);
Dec(TotalLines);
if TotalLines = 0 then begin
TotalLines := 1;
TotalBytes := 1;
BufPtr^[1] := ^Z;
end
else
Dec(TotalBytes, J);
Modified := True;
OldModified := True;
end;
end;
procedure JoinLinePrim(LineNum : Integer);
{-Primitive routine to join two lines}
var
I : Word;
begin
with EMCB do begin
{find the place to join the lines}
I := FindLineIndex(EMCB, LineNum);
{make room for a CRLF}
Move(BufPtr^[I], BufPtr^[I-2], Succ(TotalBytes-I));
Dec(TotalLines);
Dec(TotalBytes, 2);
BufPtr^[TotalBytes+1] := ^Z;
Modified := True;
OldModified := True;
end;
end;
procedure PutLineAtTop(LineNum : Integer);
{-Position the specified line at top of editing window}
begin
with EMCB do begin
if LineNum < 1 then
LineNum := 1
else if LineNum > TotalLines then
LineNum := TotalLines;
BufPosTop := FindLineIndex(EMCB, LineNum);
LineAtTop := LineNum;
RedrawScreen;
end;
end;
function GetIndent(S : string) : Byte;
{-Get the indentation level of S}
var
I : Word;
SLen : Byte absolute S;
begin
I := 0;
while S[SLen] = ' ' do
Dec(SLen);
while (I < SLen) and (S[I+1] = ' ') do
Inc(I);
GetIndent := I;
end;
procedure WrapLine(Trim : Boolean);
{-Word wrap the current line}
var
I, J : Integer;
Temp, SaveSt : string;
begin
with EMCB do begin
if TooManyLinesCheck then
Exit;
SaveSt := St;
TpString.WordWrap(St, St, Temp, Margin, False);
if IndentMode then begin
I := GetIndent(St);
if I <> 0 then
Insert(CharStr(' ', I), Temp, 1);
end;
I := Length(Temp)-(Length(SaveSt)-CurCol);
if I < 1 then
I := 1;
SaveCurrentLine(True);
DrawCurrentLine;
InsLinePrim(CurLine, StLen+1);
if OK then begin
GotoLine(CurLine+1, Trim);
St := Temp;
SaveCurrentLine(True);
ColDelta := 0;
CurCol := I;
OldCol := I;
end
else begin
St := SaveSt;
SaveCurrentLine(True);
end;
end;
end;
procedure ReformatParagraph;
{-Reformat a paragraph starting at the current line}
var
SaveMax, I : Integer;
Temp : string;
begin
with EMCB do begin
SaveCurrentLine(True);
if StLen = 0 then begin
GotoLine(CurLine+1, True);
Exit;
end;
{ignore line limit when reformatting}
SaveMax := MaxLines;
MaxLines := MaxInt;
while (CurLine < TotalLines) and (OK = True) do begin
while (StLen > Margin) and OK do
WrapLine(False);
if OK then
OK := FindLineLength(EMCB, CurLine+1) <> 0;
if OK and IndentStartsParagraph then
OK := BufPtr^[KnownOfs] <> ' ';
if OK then begin
Inc(StLen);
St[StLen] := ' ';
I := Succ(StLen);
SaveCurrentLine(False);
JoinLinePrim(CurLine+1);
LoadLine(CurLine, False);
while (I < StLen) and (St[I] = ' ') do
Delete(St, I, 1);
TrimSpaces;
end;
end;
OK := True;
while (StLen > Margin) and OK do
WrapLine(False);
RedrawScreen;
GotoLine(CurLine+1, True);
if CurLine = TotalLines then
CurCol := Succ(StLen)
else
CurCol := 1;
OldCol := CurCol;
MaxLines := SaveMax;
end;
end;
procedure DeleteWordPrim;
{-Primitive routine to delete a word}
var
DelEnd : Word;
begin
with EMCB do begin
if CurCol > StLen then
Exit;
{start deleting at the cursor}
DelEnd := CurCol;
{delete all of the current word, if any}
if St[CurCol] <> ' ' then
while (St[DelEnd] <> ' ') and (DelEnd <= StLen) do
Inc(DelEnd);
{delete any spaces prior to the next word, if any}
while (St[DelEnd] = ' ') and (DelEnd <= StLen) do
Inc(DelEnd);
Delete(St, CurCol, DelEnd-CurCol);
end;
end;
{$IFDEF UseMouse}
procedure MouseSelect;
{-Move cursor to position of mouse}
var
CurRow, TargetLine : Integer;
TargetRow, TargetCol : Integer;
begin
{convert mouse X and Y coordinates to absolute row and col}
TargetRow := MouseKeyWordY+MouseYLo;
TargetCol := MouseKeyWordX+MouseXLo;
with EMCB do
{make sure mouse is within edit window}
if (TargetCol >= XL) and (TargetCol <= XH)
and (TargetRow >= YL) and (TargetRow <= YH) then begin
{calculate current screen row}
CurRow := Word(YL)+(CurLine-LineAtTop);
{calculate target line number}
TargetLine := CurLine+(TargetRow-CurRow);
if TargetLine <= TotalLines then begin
{move cursor to desired location}
CurCol := TargetCol-Pred(XL)-ColDelta;
GotoLine(TargetLine, True);
end;
end;
end;
{$ENDIF}
procedure TopOfFile;
{-Reset for top of file}
begin
with EMCB do begin
PutLineAtTop(1);
GotoLine(1, True);
CurCol := 1;
OldCol := 1;
RedrawScreen;
end;
end;
procedure ReformatGlobally;
{-Reformat entire file}
begin
with EMCB do begin
{skip all this if the file is empty}
if TotalBytes = 1 then
Exit;
{go to top of file}
TopOfFile;
{while not at last line, reformat paragraphs}
while CurLine < TotalLines do
ReformatParagraph;
end;
end;
procedure CheckLineLimit;
{-Display error message if line limit exceeded}
begin
with EMCB do
if TotalLines > MaxLines then begin
RedrawScreen;
CallErrorRoutine(tmOverLineLimit);
end;
end;
begin
with EMCB do begin
{Store cursor position and shape}
GetCursorState(CursorXY, CursorSL);
{Save break checking state}
SaveBreak := CheckBreak;
CheckBreak := False;
{set cursor shape}
InsertMode := not InsertMode;
ToggleInsertMode;
{initialize miscellaneous variables}
WinWidth := Succ(XH-XL);
NextUserCmd := 1;
KnownLine := 1;
KnownOfs := 1;
OldModified := Modified;
ReadOnlyMode := ReadOnly;
{$IFDEF UseMouse}
SaveMouse := MouseCursorOn;
{$ENDIF}
{get the first line}
LoadLine(EMCB.CurLine, True);
{draw whole screen}
ForceRedraw := True;
{see if we exceeded the line limit}
CheckLineLimit;
{loop while reading keys}
Done := False;
DoingChars := False;
repeat
OK := True;
{update screen}
if CurCol > MaxLineLength+1 then
CurCol := MaxLineLength+1;
if CurCol > WinWidth+ColDelta then begin
ColDelta := CurCol-WinWidth;
RedrawScreen;
end
else if CurCol < Succ(ColDelta) then begin
ColDelta := Pred(CurCol);
RedrawScreen;
end
else if ForceRedraw then
RedrawScreen
else
DrawCurrentLine;
{position cursor}
GoToXYAbs(XL+Pred(CurCol)-ColDelta, YL+(CurLine-LineAtTop));
{set modified flag}
TrimSpaces;
Modified := OldModified or (St <> OldSt);
{display status line}
if MemoStatusPtr <> nil then begin
{update TotalBytes field for status routine}
J := TotalBytes;
Inc(TotalBytes, Integer(StLen)-Length(OldSt));
{call status routine}
StatusRoutine(EMCB);
{reset TotalBytes field}
TotalBytes := J;
end;
{$IFDEF UseMouse}
if MemoMouseEnabled then begin
SaveWaitState := WaitForButtonRelease;
WaitForButtonRelease := True;
end;
{$ENDIF}
{see if there is a user command left to process}
EMC := UserCmdList[NextUserCmd];
if DoingChars then begin
if EMC = EMchar then begin
{EMchar acts as toggle}
EMC := EMnone;
DoingChars := False;
end
else begin
{treat the command as a character}
Ch := Char(EMC);
EMC := EMchar;
end;
Inc(NextUserCmd);
end
else if EMC = EMnone then
{read from the keyboard}
EMC := GetCommand(MemoKeySet, MemoKeyPtr, ChWord)
else begin
{process next user command}
Inc(NextUserCmd);
if EMC = EMchar then begin
DoingChars := True;
EMC := EMnone;
end;
end;
{make sure command is allowable if in read-only mode}
if ReadOnlyMode then
if EMC in DisallowedInReadOnlyMode then
EMC := EMnone;
{reinterpret potentially troublesome control characters}
if EMC = EMchar then
case Ch of
^M : EMC := EMenter;
^J, ^Z : EMC := EMnone;
end;
{$IFDEF UseMouse}
if MemoMouseEnabled then
WaitForButtonRelease := SaveWaitState;
{$ENDIF}
{deal with control characters if desired}
if EMC = EMctrlChar then
{don't allow control characters if attributes are the same}
if (CAttr = TAttr) then
EMC := EMnone
else begin
BlockCursor;
ChWord := GetKey;
EMC := EMchar;
if InsertMode then
FatCursor
else
NormalCursor;
end;
case EMC of
EMchar : {A character to enter the string}
if CurCol <= MaxLineLength then begin
if CurCol > StLen then
FillChar(St[Succ(StLen)], CurCol-StLen, ' ');
if not InsertMode then begin
{overtype mode}
if (CurCol <= MaxLineLength) then begin
St[CurCol] := Ch;
if (Ch <> ' ') and (CurCol > StLen) and InsertOK(CurCol-StLen) then
StLen := CurCol;
Inc(CurCol);
end;
end
else if StLen < MaxLineLength then begin
{insert mode}
if CurCol > StLen then begin
if Ch = ' ' then
Inc(CurCol)
else if InsertOK(CurCol-StLen) then begin
StLen := CurCol;
St[CurCol] := Ch;
Inc(CurCol);
end;
end
else if InsertOK(1) then begin
Insert(Ch, St, CurCol);
Inc(CurCol);
end;
end;
if WordWrap and (CurCol > Margin) and (StLen > Margin) then begin
WrapLine(True);
ForceRedraw := True;
end;
end;
EMenter : {new line}
begin
I := GetIndent(St);
if InsertMode then begin
if IndentMode and (CurCol <= StLen) and (I > 0) then
Insert(CharStr(' ', I), St, CurCol);
SaveCurrentLine(True);
if CurCol > StLen then
CurCol := Succ(StLen);
InsLinePrim(CurLine, CurCol);
end;
if OK then begin
GotoLine(CurLine+1, True);
if IndentMode and InsertMode then
CurCol := Succ(I)
else
CurCol := 1;
OldCol := CurCol;
if InsertMode then
ForceRedraw := True;
end;
end;
EMuser0..EMuser9, {user-defined exit commands}
EMquit : {exit from editor}
begin
SaveCurrentLine(True);
Done := True;
end;
EMhome : {Cursor to beginning of line}
CurCol := 1;
EMend : {Cursor to end of line}
CurCol := Succ(StLen);
EMdelEol : {Delete from cursor to end of line}
if StLen > CurCol then
StLen := Pred(CurCol);
EMdelLine : {Delete entire line}
if CurLine = TotalLines then begin
StLen := 0;
CurCol := 1;
SaveCurrentLine(True);
end
else begin
DelLinePrim(CurLine);
CurCol := 1;
LoadLine(CurLine, True);
ForceRedraw := True;
end;
EMrestore : {Restore default and continue}
begin
St := OldSt;
CurCol := OldCol;
end;
EMleft : {Cursor left by one character}
if CurCol > 1 then
Dec(CurCol);
EMright : {Cursor right by one character}
Inc(CurCol);
EMup : {Cursor up one line}
if CurLine > 1 then
GotoLine(CurLine-1, True);
EMdown : {Cursor down one line}
if CurLine < TotalLines then
GotoLine(CurLine+1, True);
EMscrollUp : {Scroll display up one line}
if LineAtTop > 1 then begin
ScrollDisplay(-1);
I := LineAtTop+(YH-YL);
if CurLine > I then
GotoLine(I, True);
end;
EMscrollDown : {Scroll display down one line}
if LineAtTop < TotalLines then begin
ScrollDisplay(1);
if CurLine < LineAtTop then
GotoLine(LineAtTop, True);
end;
EMpageUp : {Scroll display up one page}
if LineAtTop > 1 then begin
I := (YH-YL);
if I > CurLine then begin
PutLineAtTop(1);
GotoLine(1, True);
end
else begin
J := CurLine-LineAtTop;
PutLineAtTop(LineAtTop-I);
GotoLine(LineAtTop+J, True);
end;
end
else
GotoLine(1, True);
EMpageDown : {Scroll display down one page}
if LineAtTop < TotalLines then begin
I := (YH-YL);
if TotalLines <= Succ(I) then begin
PutLineAtTop(TotalLines);
GotoLine(TotalLines, True);
end
else begin
J := CurLine-LineAtTop;
PutLineAtTop(LineAtTop+I);
GotoLine(LineAtTop+J, True);
end;
end;
EMscreenTop : {Cursor to top of screen}
GotoLine(LineAtTop, True);
EMscreenBot : {Cursor to bottom of screen}
GotoLine(LineAtTop+(YH-YL), True);
EMtopOfFile : {Cursor to top of file}
TopOfFile;
EMendOfFile : {Cursor to bottom of file}
begin
I := YH-YL;
if CurLine < TotalLines-I then
PutLineAtTop(TotalLines-I);
GotoLine(TotalLines, True);
CurCol := Succ(StLen);
OldCol := CurCol;
end;
EMtab : {Tab}
begin
I := Succ(Succ(CurCol div TabDelta) * TabDelta);
if (not InsertMode) or (CurCol > StLen) then
CurCol := I
else if (CurCol <= StLen) then begin
if InsertOK(I-CurCol) and (Margin-StLen > I-CurCol) then begin
Insert(CharStr(' ', I-CurCol), St, CurCol);
CurCol := I;
end;
end
end;
EMwordLeft : {Cursor left one word}
if CurCol > 1 then begin
Dec(CurCol);
while (CurCol >= 1) and ((CurCol > StLen) or (St[CurCol] = ' ')) do
Dec(CurCol);
while (CurCol >= 1) and (St[CurCol] <> ' ') do
Dec(CurCol);
Inc(CurCol);
end
else if CurLine > 1 then begin
GotoLine(CurLine-1, True);
CurCol := Succ(StLen);
OldCol := CurCol;
end;
EMwordRight : {Cursor right one word}
begin
if CurCol < StLen then begin
Inc(CurCol);
while (CurCol <= StLen) and (St[CurCol] <> ' ') do
Inc(CurCol);
while (CurCol <= StLen) and (St[CurCol] = ' ') do
Inc(CurCol);
end
else if CurLine < TotalLines then begin
GotoLine(CurLine+1, True);
CurCol := 1;
OldCol := 1;
end;
end;
EMdel : {Delete current character}
if CurCol <= StLen then
Delete(St, CurCol, 1);
EMback : {Backspace one character}
if CurCol > 1 then begin
Dec(CurCol);
Delete(St, CurCol, 1);
end
else if CurLine > 1 then begin
GotoLine(CurLine-1, True);
CurCol := Succ(StLen);
JoinLinePrim(CurLine+1);
LoadLine(CurLine, True);
ForceRedraw := True;
OldCol := CurCol;
end;
EMdelWord : {Delete word to right of cursor}
begin
if CurCol <= StLen then
DeleteWordPrim
else if CurLine < TotalLines then
if InsertOK(CurCol-StLen) then begin
FillChar(St[Succ(StLen)], CurCol-StLen, ' ');
StLen := Pred(CurCol);
I := CurCol;
SaveCurrentLine(False);
JoinLinePrim(CurLine+1);
LoadLine(CurLine, True);
CurCol := I;
ForceRedraw := True;
OldCol := CurCol;
end;
end;
EMins : {Toggle insert mode}
ToggleInsertMode;
EMindent : {Toggle auto-indent mode}
IndentMode := not IndentMode;
EMwordWrap : {Toggle word wrap}
WordWrap := not WordWrap;
EMreformatP : {Reformat paragraph}
begin
ReformatParagraph;
CheckLineLimit;
end;
EMreformatG : {Global reformat}
begin
ReformatGlobally;
CheckLineLimit;
end;
{$IFDEF UseMouse}
EMmouse : {Mouse select}
if MemoMouseEnabled then
MouseSelect;
{$ENDIF}
EMhelp : {Help}
if MemoHelpPtr <> nil then
HelpRoutine(HelpForMemo, @EMCB, HelpTopic);
end;
until Done;
{redraw the screen one last time}
RedrawScreen;
{restore break checking status}
CheckBreak := SaveBreak;
{Restore cursor position and shape}
RestoreCursorState(CursorXY, CursorSL);
{$IFDEF UseMouse}
ShowMousePrim(SaveMouse);
{$ENDIF}
{return exit code}
EditMemo := EMC;
end;
end;
function AddMemoCommand(Cmd : EMtype; NumKeys : Byte; Key1, Key2 : Word) : Boolean;
{-Add a new command key assignment or change an existing one}
begin
AddMemoCommand :=
AddCommandPrim(MemoKeySet, MemoKeyMax, Cmd, NumKeys, Key1, Key2);
end;
{$IFDEF UseMouse}
procedure EnableMemoMouse;
{-Enable mouse support in TPMEMO}
begin
if MouseInstalled and not MemoMouseEnabled then begin
MemoKeyPtr := @ReadKeyOrButton;
EnableEventHandling;
MemoMouseEnabled := True;
end;
end;
procedure DisableMemoMouse;
{-Disable mouse support in TPMEMO}
begin
if MemoMouseEnabled then begin
MemoKeyPtr := @ReadKeyWord;
DisableEventHandling;
MemoMouseEnabled := False;
end;
end;
{$ENDIF}
function ReadMemoFile(var Buffer; BufferSize : Word;
FName : string; var FSize : LongInt) : MemoStatusType;
{-Read a file into Buffer, returning a status code}
var
Buf : array[1..65521] of Char absolute Buffer;
F : file;
I, BytesRead, BytesToRead : Word;
MaxSize : LongInt;
begin
ReadMemoFile := mstNotFound;
FSize := 0;
Buf[1] := ^Z;
if Length(FName) = 0 then
Exit;
{try to open file}
Assign(F, FName);
Reset(F, 1);
I := IoResult;
{check for invalid pathname}
if I = 3 then
ReadMemoFile := mstInvalidName;
if I <> 0 then
Exit;
{check the file size}
FSize := FileSize(F);
MaxSize := LongInt(BufferSize)-Succ(SafetyMargin);
if (FSize <= MaxSize) then
BytesToRead := FSize
else if AllowTruncation then
BytesToRead := MaxSize
else begin
{file too big}
ReadMemoFile := mstTooLarge;
Close(F);
I := IoResult;
Exit;
end;
{read the file into the buffer}
BlockRead(F, Buf, BytesToRead, BytesRead);
if (BytesRead <> BytesToRead) then begin
ReadMemoFile := mstReadError;
Close(F);
I := IoResult;
end
else begin
Close(F);
if IoResult = 0 then
if FSize > MaxSize then
ReadMemoFile := mstTruncated
else
ReadMemoFile := mstOK
else
ReadMemoFile := mstCloseError;
end;
{make sure there's a ^Z at the end of the buffer}
Buf[FSize+1] := ^Z;
end;
function SaveMemoFile(var EMCB : EMcontrolBlock; FName : string;
MakeBackup : Boolean) : MemoStatusType;
{-Save the current file in the text buffer associated with EMCB}
var
F : file;
I, BytesWritten : Word;
function Exist(FName : string; var F : file) : Boolean;
{-Return true and assigned file handle if file exists}
var
I : Word;
begin
Assign(F, FName);
Reset(F);
Exist := (IoResult = 0);
Close(F);
I := IoResult;
end;
procedure MakeBakFile(NewName : string);
{-Make a backup file}
var
NF, BF : file;
BakName : string;
begin
if Exist(NewName, NF) then begin
BakName := ForceExtension(NewName, 'BAK');
if Exist(BakName, BF) then
Erase(BF);
Rename(NF, BakName);
end;
end;
begin
with EMCB do begin
if MakeBackup then
MakeBakFile(FName);
Assign(F, FName);
Rewrite(F, 1);
if IoResult <> 0 then begin
SaveMemoFile := mstCreationError;
Close(F);
I := IoResult;
Exit;
end;
BlockWrite(F, BufPtr^, TotalBytes, BytesWritten);
if (BytesWritten <> TotalBytes) or (IoResult <> 0) then begin
SaveMemoFile := mstWriteError;
Close(F);
Exit;
end;
Close(F);
if IoResult <> 0 then begin
SaveMemoFile := mstCloseError;
Exit;
end;
{reset modified flag}
Modified := False;
SaveMemoFile := mstOK;
end;
end;
begin
{initialize pointer to keyboard input routine}
MemoKeyPtr := @ReadKeyWord;
end.